package com.liuxing.javaSpringBoot.modules.account.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.liuxing.javaSpringBoot.modules.account.dao.UserDao;
import com.liuxing.javaSpringBoot.modules.account.dao.UserRoleDao;
import com.liuxing.javaSpringBoot.modules.account.entity.User;
import com.liuxing.javaSpringBoot.modules.account.entity.UserRole;
import com.liuxing.javaSpringBoot.modules.account.service.UserService;
import com.liuxing.javaSpringBoot.modules.common.entity.Result;
import com.liuxing.javaSpringBoot.modules.common.entity.SearchBean;
import com.liuxing.javaSpringBoot.util.MD5Util;
import com.liuxing.javaSpringBoot.util.RedisUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * Created by IntelliJ IDEA.
 * @File UserServiceImpl.java
 * @Auth liuxing
 * @Date 2021/8/22 11:22
 * @Email liuxing997@foxmail.com
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private UserRoleDao userRoleDao;

    @Autowired
    private RedisUtils redisUtils;

    //用户登录
    @Override
    public Result<User> login(User user) {
        int loginFailMaxCount = 5;
        // 从数据库获取 错误登录次数
        String key = String.format("failed_login_count_%s", user.getUserName());
        int loginFailCount = redisUtils.get(key) == null ? 0 : (Integer)redisUtils.get(key);
        // 如果错误登录次数 > Max，直接返回错误信息
        if (loginFailCount >= loginFailMaxCount) {
            return new Result<User>(Result.ResultCode.FAILED.code,
                    String.format("该账户连续登录错误 %s 次，账户锁定 30 s.", loginFailCount));
        }
        //获取当前用户
        Subject subject =  SecurityUtils.getSubject();
        //判断当前用户是否认证过
        if (!subject.isAuthenticated()){
            //创建一个认证令牌
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword());
            //设置记住我
            token.setRememberMe(user.getRememberMe());
            try{
                //登录
                subject.login(token);
                //资源授权
                subject.checkRoles();
                //设置session
                subject.getSession().setAttribute("user",subject.getPrincipal());
                //登录成功，将错误次数归 0
                redisUtils.set(key, 0);
            }catch (UnknownAccountException e){
                //未知账号异常
                return new Result<>(Result.ResultCode.User_Does_Not_Exist.code, "用户不存在！");
            }catch (IncorrectCredentialsException e){
                //密码错误异常
                // redis 里错误的登录次数 +1
                loginFailCount += 1;
                redisUtils.set(key, loginFailCount);
                // 如果错误登录次数 + 1 == max， redis 设置该 key 的过期时间
                if (loginFailCount < loginFailMaxCount) {
                    return new Result<User>(Result.ResultCode.Wrong_Password.code,
                            String.format("密码错误！还可以试 %s 次", (loginFailMaxCount - loginFailCount)));
                } else {
                    // 返回错误消息
                    redisUtils.expire(key, 30);
                    return new Result<User>(Result.ResultCode.Wrong_Password.code,
                            String.format("该账户连续登录错误 %s 次，账户锁定 30 s.", loginFailCount));
                }
            }catch (AuthenticationException e){
                //认证异常
                return new Result<>(Result.ResultCode.Login_Failed.code, "登录失败！");
            }
        }
        return new Result<User>(Result.ResultCode.SUCCESS.code, "登录成功！",(User) subject.getPrincipal());
    }

    //用户登出
    @Override
    public Result<Object> logout() {
        Subject subject = SecurityUtils.getSubject();
        //用户登出
        subject.logout();
        return new Result<>(Result.ResultCode.SUCCESS.code, "退出成功，欢迎再次使用！");
    }

    //添加用户信息
    @Override
    @Transactional
    public Result<User> insertUser(User user) {
        List<User> users = Optional
                .ofNullable(userDao.getUserByUserNameOrEmail(user.getEmail(), user.getUserName()))
                .orElse(Collections.emptyList());
        if (users.size() > 0 ){
           return new Result<>(Result.ResultCode.Username_Is_Exist.code,
                    "用户名或邮箱已被使用！");
        }
        user.setCreateDate(LocalDateTime.now());
        user.setPassword(MD5Util.md5Hash(user.getPassword(),user.getUserName()));
        userDao.insertUser(user);
        if (user.getRoles() != null) {
            user.getRoles().stream()
                    .forEach(item -> {userRoleDao.insertUserRole(new UserRole(user.getId(), item.getId()));});
        }
        return new Result<User>(Result.ResultCode.SUCCESS.code,
                "添加成功！", user);
    }

    //根据用户名查询用户信息
    @Override
    public User getUserByUsername(String name) {
        return userDao.getUserByUsername(name);
    }

    //根据用户ID修改用户信息
    @Override
    @Transactional
    public Result<User> updateUserById(User user) {
        List<User> users = Optional
                .ofNullable(userDao.getUserByUserNameOrEmail(user.getEmail(), user.getUserName()))
                .orElse(Collections.emptyList());
        if (users.stream().filter(item -> item.getId() != user.getId()).count() > 0){
            return new Result<>(Result.ResultCode.Username_Is_Exist.code,
                    "用户名或邮箱已被使用！");
        }
        if (!user.getPassword().equals(userDao.getUserByUserId(user.getId()).getPassword())){
            user.setPassword(MD5Util.md5Hash(user.getPassword(),user.getUserName()));
        }
        userDao.updateUserById(user);
        if (user.getRoles() != null && !user.getRoles().isEmpty()) {
            userRoleDao.deleteUserRoleByUserId(user.getId());
            user.getRoles().stream()
                    .forEach(item -> {userRoleDao.insertUserRole(new UserRole(user.getId(), item.getId()));});
        }
        return new Result<>(Result.ResultCode.SUCCESS.code,
                "Update success.", user);
    }

    //根据用户ID删除用户
    @Override
    @Transactional
    public Result<Object> deleteUserByUserId(int id) {
        userDao.deleteUserByUserId(id);
        userRoleDao.deleteUserRoleByUserId(id);
        return new Result<>(Result.ResultCode.SUCCESS.code,
                "删除成功！");
    }

    //根据用户ID查询用户
    @Override
    public User getUserByUserId(int id) {
        return userDao.getUserByUserId(id);
    }

    //查询所有用户信息
    @Override
    public List<User> getAllUser() {
        return Optional
                .ofNullable(userDao.getAllUser())
                .orElse(Collections.emptyList());
    }

    //批量插入用户信息
    @Override
    @Transactional
    public Result<Object> insertUsers(List<User> users) {
        users.stream().forEach(item ->{
            item.setCreateDate(LocalDateTime.now());
            item.setPassword(MD5Util.md5Hash(item.getPassword(),item.getUserName()));
        });
        userDao.insertUsers(users);
        return new Result<>(Result.ResultCode.SUCCESS.code,
                "Insert Users success.");
    }

    //分页查询用户信息
    @Override
    public PageInfo<User> getUsersBySearchBean(SearchBean searchBean) {
        searchBean.initSearchBean();
        PageHelper.startPage(searchBean.getCurrentPage(),searchBean.getPageSize());
        return new  PageInfo<> (Optional
                .ofNullable(userDao.getUsersBySearchBean(searchBean))
                .orElse(Collections.emptyList()));
    }
}
